package com.example.dbsample.config.apollo;

import com.ctrip.framework.apollo.Config;
import com.ctrip.framework.apollo.ConfigService;
import com.ctrip.framework.apollo.model.ConfigChange;
import com.ctrip.framework.apollo.model.ConfigChangeEvent;
import com.ctrip.framework.apollo.spring.annotation.ApolloConfigChangeListener;
import com.example.dbsample.service.ConsumerExecutorService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang.StringUtils;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.context.environment.EnvironmentChangeEvent;
import org.springframework.cloud.context.scope.refresh.RefreshScope;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

/**
 * 每次更新依靠@Value  不一定获取到最新的值。重复修改几次，有事可以有事不行，
 * 靠changeEvent 获取的新旧值更稳妥些。
 */
@Slf4j
@Service
public class ApolloConfigRefresh implements ApplicationContextAware {
    @Autowired
    private RefreshScope refreshScope;

    @Autowired
    private ConsumerExecutorService consumerExecutorService;

    private ApplicationContext applicationContext;

    @Bean
    public void config() {
        Config config = ConfigService.getConfig("application");
        String propertyTestApolloKey = config.getProperty("test_apollo_key", StringUtils.EMPTY);
        System.out.println("propertyTestApolloKey: " + propertyTestApolloKey);
    }

    // TODO 使用虚假的namespace，会报错
    /**
     *
     * TODO 使用虚假的namespace，会报错：* @ApolloConfigChangeListener(value = {"application", "mock_namespace"})
     * Apollo的值改变时，做记录、打印等处理
     * TODO 文中还有推送事件的处理，但暂时不清楚是做什么的
     * value变量：填入namespace名称
     * @param event
     */
    @ApolloConfigChangeListener(value = {"application"})
    public void refreshApolloConfig(ConfigChangeEvent event) {
        refreshScope.refreshAll();
        // TODO 这里要加上监听器
        this.applicationContext.publishEvent(new EnvironmentChangeEvent(event.changedKeys()));

        /**
         * 监听具体的变量
         * TODO 即便是这里处理过了，下面的循环还是会再处理一次，并不是取了之后就没有了
          */
        if(event.isChanged("test_apollo_key")) {
            ConfigChange changeTestApolloKey = event.getChange("test_apollo_key");
            String newValue = changeTestApolloKey.getNewValue();
            System.out.println("newValue = " + newValue);
        }

        ConfigChange change;
        for (String key : event.changedKeys()) {
            change = event.getChange(key);
            // 打印改动的配置信息
            System.out.println(
                    String.format(String.format("Change - key: %s, oldValue: %s, newValue: %s, changeType: %s",
                            change.getPropertyName(), change.getOldValue(), change.getNewValue(),
                            change.getChangeType())));
        }

        this.executeEvent(event);
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    private void executeEvent(ConfigChangeEvent event) {
        for (String changedKey : event.changedKeys()) {
            if("test_apollo_switch".equals(changedKey)) {
                consumerExecutorService.execute();
            }
        }
    }
}
